package gt

import (
	"strconv"

	"gitee.com/dark.H/gs"
	termbox "github.com/nsf/termbox-go"
)

type ListPanel[T any] struct {
	ShowBar bool
	ShowNum bool
	Loc     *PanelLoc
	Size    int
	Window  int
	Cursor  int
	Pos     int
	keyNow  int
	Mode    int

	Items      gs.List[T]
	ExtendKeys map[int]func(key, cursor int, now any) bool
	Selected   T
	Label      string
	savedKeys  string
	SearchKey  gs.Str
}

func NewListPanel[T any](label string, items gs.List[T], windowSize int) (p *ListPanel[T]) {
	return &ListPanel[T]{

		Items: items,
		Loc: &PanelLoc{
			X: 1,
			Y: 1,
		},
		Size:   items.Count(),
		Label:  label,
		Window: windowSize,

		ExtendKeys: make(map[int]func(key int, cursor int, now any) bool),
		ShowBar:    true,
		ShowNum:    true,
		Selected:   items[0],
	}
}
func (draw *ListPanel[T]) SetTopOffset(i int) {
	draw.Loc.Y += i
}
func (draw *ListPanel[T]) SetLeftOffset(i int) {
	draw.Loc.X += i
}

func (draw *ListPanel[T]) Add(one T) {
	draw.Items = draw.Items.Add(one)
	draw.Size += 1
}

func (draw *ListPanel[T]) WithSubPanel(subpanelDo func()) *ListPanel[T] {

	defer func() {
		termbox.Init()
		draw.Mode = 0
		draw.Draw()
	}()
	draw.Mode = 1
	subpanelDo()
	return draw
}

func (draw *ListPanel[T]) Draw() {
	if draw.Size == 0 {
		return
	}
	draw.Loc.maxRow, draw.Loc.maxWidth = gs.GetWindowsSize()
	bar := int(draw.Window * draw.Window / draw.Size)
	bar_start := int(draw.Pos * draw.Window / draw.Size)
	bar_end := bar_start + bar
	draw_num := 0

	draw.Loc.Cusor().Reset().A(gs.Str(draw.Label).Color("g") + "  line:" + gs.S(draw.Cursor).Color("y", "F") + " Search:" + gs.S(draw.SearchKey).Color("B", "F", "b")).Print(true)
	if draw.SearchKey.Len() > 0 {

		filter := gs.List[T]{}
		draw.Items.Every(func(no int, i T) {
			if gs.S(i).In(draw.SearchKey) {
				filter = filter.Add(i)
			}
		})

		bar = int(draw.Window * draw.Window / filter.Count())
		bar_start := int(draw.Pos * draw.Window / filter.Count())
		bar_end := bar_start + bar
		draw_num := 0
		filter.Every(func(no int, i T) {
			// fmt.Println("i", i)
			if no < draw.Pos {
				return
			}

			if no >= draw.Pos+draw.Window {
				return
			}
			// fmt.Println("ok")
			pre := " "
			if draw_num >= bar_start && draw_num <= bar_end && draw.ShowBar {

				if draw_num == bar_start {
					pre = "╦"
				} else if draw_num == bar_end {
					pre = "╩"
				} else {
					pre = "║"
				}
			}
			mid := ""
			if draw.ShowNum {
				mid = gs.Str(" %3d ").F(no).Color("B").Str()
			}
			if no == draw.Cursor {
				w := draw.Loc.Line(draw_num+1).Str() + gs.Str(pre+" %s%s").F(mid, gs.S(i).Color("B", "U", "g"))
				if draw.Loc.maxWidth-w.Len() > 5 {
					w += gs.Str(" ").Repeat(draw.Loc.maxWidth - w.Len() - 5)
				}
				(w).Print(true)
				draw.Loc.locStr = gs.Str("")

			} else {
				w := draw.Loc.Line(draw_num+1).Str() + gs.Str(pre+" %s%s").F(mid, i)
				if draw.Loc.maxWidth-w.Len() > 5 {
					w += gs.Str(" ").Repeat(draw.Loc.maxWidth - w.Len() - 5)
				}
				(w).Print(true)
				draw.Loc.locStr = gs.Str("")
				// gs.S(i).Println()
			}
			draw_num += 1
		})

	} else {
		draw.Items.Every(func(no int, i T) {
			// fmt.Println("i", i)
			if no < draw.Pos {
				return
			}

			if no >= draw.Pos+draw.Window {
				return
			}
			// fmt.Println("ok")
			pre := " "
			if draw_num >= bar_start && draw_num <= bar_end && draw.ShowBar {

				if draw_num == bar_start {
					pre = "╦"
				} else if draw_num == bar_end {
					pre = "╩"
				} else {
					pre = "║"
				}
			}
			mid := ""
			if draw.ShowNum {
				mid = gs.Str(" %3d ").F(no).Color("B").Str()
			}
			if no == draw.Cursor {
				w := draw.Loc.Line(draw_num+1).Str() + gs.Str(pre+" %s%s").F(mid, gs.S(i).Color("B", "U", "g"))
				if draw.Loc.maxWidth-w.Len() > 5 {
					w += gs.Str(" ").Repeat(draw.Loc.maxWidth - w.Len() - 5)
				}
				(w).Print(true)
				draw.Loc.locStr = gs.Str("")

			} else {
				w := draw.Loc.Line(draw_num+1).Str() + gs.Str(pre+" %s%s").F(mid, i)
				if draw.Loc.maxWidth-w.Len() > 5 {
					w += gs.Str(" ").Repeat(draw.Loc.maxWidth - w.Len() - 5)
				}
				(w).Print(true)
				draw.Loc.locStr = gs.Str("")
				// gs.S(i).Println()
			}
			draw_num += 1
		})

	}
}

func (draw *ListPanel[T]) GetFilter() (e gs.List[T]) {
	draw.Items.Every(func(no int, i T) {
		if gs.S(i).In(draw.SearchKey) {
			e = e.Add(i)
		}
	})
	return
}

func (draw *ListPanel[T]) Down() {
	draw.Cursor += 1
	if draw.SearchKey.Len() > 0 {
		fs := draw.GetFilter()
		if draw.Cursor == fs.Count() {
			draw.Cursor = 0
			draw.Pos = 0
		}
		// c = 10 , pos + 1
		if draw.Cursor >= draw.Window+draw.Pos {

			draw.Pos += 1
		}
	} else {
		if draw.Cursor == draw.Size {
			draw.Cursor = 0
			draw.Pos = 0
		}
		// c = 10 , pos + 1
		if draw.Cursor >= draw.Window+draw.Pos {

			draw.Pos += 1
		}
	}

}

func (draw *ListPanel[T]) Up() {
	draw.Cursor -= 1
	if draw.SearchKey.Len() > 0 {
		fs := draw.GetFilter()
		if draw.Cursor < 0 {
			draw.Cursor = fs.Count() + draw.Cursor
			draw.Pos = draw.Cursor - draw.Window + 1
		}
		if draw.Cursor < draw.Pos {
			draw.Pos -= 1
		}

	} else {
		if draw.Cursor < 0 {
			draw.Cursor = draw.Size + draw.Cursor
			draw.Pos = draw.Cursor - draw.Window + 1
		}
		if draw.Cursor < draw.Pos {
			draw.Pos -= 1
		}

	}
}

func (draw *ListPanel[T]) SetLabel(msg gs.Str) {
	draw.Label = msg.Str()
}

func (draw *ListPanel[T]) OnKey(k termbox.Event) bool {
	// suffix = gs.Str("")
	draw.keyNow = int(k.Key)
	// fmt.Println(k)
	// fmt.Println(k)
	// time.Sleep(3 * time.Second)
	if k.Key == termbox.KeyEnter {
		return true
	}
	switch k.Ch {
	case 'j':
		if draw.savedKeys == "" {
			draw.savedKeys = "1"
		}
		ecou, err := strconv.Atoi(draw.savedKeys)
		if err != nil {
			ecou = 1
		}
		for i := 0; i < ecou; i++ {
			draw.Down()
		}
		draw.Selected = draw.Items[draw.Cursor]
		// now = draw.Cursor
		draw.savedKeys = ""
	case '/':
		k := InputLastLine()
		termbox.Init()
		draw.SearchKey = k
		draw.Pos = 0

	case 'n':
		k := draw.SearchKey
		e := draw.Cursor
		now := draw.Cursor
		draw.Items[now:].Any(func(no int, i T) bool {
			if gs.S(i).In(k.Str()) {
				e = no + now
				return true
			}
			return false
		})
		if e > draw.Cursor {
			for i := 0; i < e-draw.Cursor; i++ {
				draw.Down()
			}
			draw.Selected = draw.Items[draw.Cursor]
		} else if e < draw.Cursor {
			for i := 0; i < draw.Cursor-e; i++ {
				draw.Up()
			}
			draw.Selected = draw.Items[draw.Cursor]
		}

	case 'k':
		if draw.savedKeys == "" {
			draw.savedKeys = "1"
		}
		ecou, err := strconv.Atoi(draw.savedKeys)
		if err != nil {
			ecou = 1
		}
		for i := 0; i < ecou; i++ {
			draw.Up()
		}

		draw.Selected = draw.Items[draw.Cursor]
		// now = panel.Cursor
		draw.savedKeys = ""

	case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0':
		draw.savedKeys += string(k.Ch)

	default:
		if call, ok := draw.ExtendKeys[int(k.Key)]; ok {
			if call(int(k.Key), draw.Cursor, draw.Items[draw.Cursor]) {
				// fmt.Println(k)
				// time.Sleep(5 * time.Second)
				return true
			}
		} else {
			switch k.Key {
			case 127:
				draw.SearchKey = draw.SearchKey.Slice(0, -1)
			case 13:
				draw.Selected = draw.Items[draw.Cursor]
				return true
			case 14:

				ecou := draw.Window
				for i := 0; i < ecou; i++ {
					draw.Down()
				}

				// now = draw.Cursor
				draw.savedKeys = ""

			case 16:
				ecou := draw.Window
				for i := 0; i < ecou; i++ {
					draw.Up()
				}

				// now = draw.Cursor
				draw.savedKeys = ""
			default:
				// draw.SetLabel(gs.S(k))

			}
		}
		// suffix = gs.Str(" ( %s %d [%s] ) ").F(gs.Str(k.Ch).Color("B", "y"), k.Ch, fmt.Sprint(k))
	}

	draw.Draw()
	return false

}

func (panel *ListPanel[T]) ExtendKey(k int, c func(key, cursor int, now any) bool) {
	panel.ExtendKeys[k] = c
}
